package com.tianji.learning.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.api.dto.trade.OrderBasicDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningPlanDTO;
import com.tianji.learning.domain.dto.LearningPlanPageVO;
import com.tianji.learning.domain.dto.LearningPlanVO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.tianji.learning.enums.LessonStatus.LEARNING;
import static com.tianji.learning.enums.LessonStatus.NOT_BEGIN;
import static com.tianji.learning.enums.PlanStatus.PLAN_RUNNING;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author WFan
 * @since 2025-03-22
 */
@Service
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {

    private final CourseClient courseClient;
    private final CatalogueClient catalogueClient;
    private final LearningRecordMapper learningRecordMapper;

    /**
     * 添加课表
     *
     * @param orderBasicDTO
     */
    @Override
    @Transactional
    public void add(OrderBasicDTO orderBasicDTO) {
        // 1.根据课程id查询课程信息
        List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(orderBasicDTO.getCourseIds());

        // 2.判断课程是否存在
        if (ObjectUtil.isEmpty(simpleInfoList)) {
            log.warn("课程不存在");
            return;
        }

        // 3.组装数据
        // 避免频繁扩容 ！！！
        List<LearningLesson> lessons = new ArrayList<>(simpleInfoList.size());
        for (CourseSimpleInfoDTO courseSimpleInfoDTO : simpleInfoList) {
            if (ObjectUtil.isEmpty(courseSimpleInfoDTO)) {
                continue;
            }
            LearningLesson lesson = new LearningLesson();
            lesson.setCourseId(courseSimpleInfoDTO.getId());
            lesson.setUserId(orderBasicDTO.getUserId());

            // 课程有效期数据判断 ！！！
            Integer validDuration = courseSimpleInfoDTO.getValidDuration();
            if (ObjectUtil.isEmpty(validDuration) || validDuration <= 0) {
                continue;
            }
            if (validDuration < 9999) {
                lesson.setExpireTime(LocalDateTime.now().minusMonths(validDuration));
            }
            lessons.add(lesson);
        }

        // 4.保存到数据库中
        saveBatch(lessons);
    }

    /**
     * 课程列表查询
     *
     * @return
     */
    @Override
    public PageDTO lesesonsListQuery(PageQuery pageQuery) {
        // 1.获取userId
        Long userId = UserContext.getUser();
        if (ObjectUtil.isEmpty(userId)) {
            log.warn("用户信息获取失败");
            return null;
        }

        // 2.1根据userId查询课程列表
        Page<LearningLesson> page = pageQuery.toMpPage(pageQuery.getSortBy(), pageQuery.getIsAsc());
        Page<LearningLesson> lessonPage = this.lambdaQuery().eq(LearningLesson::getUserId, userId)
                .page(page);


        List<LearningLesson> records = lessonPage.getRecords();
        if (ObjectUtil.isEmpty(records)) {
            log.warn("用户没有课程");
            return PageDTO.empty(lessonPage);
        }
        // 2.2获取课程id
        Set<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());


        // 3.根据课程id查询课程信息
        Map<Long, CourseSimpleInfoDTO> courseInfoMap = null;
        try {
            courseInfoMap = selectLessonAnd2Map(courseIds);
        } catch (Exception e) {
            return PageDTO.empty(lessonPage);
        }

        // 3.组装数据
        List<LearningLessonVO> vos = new ArrayList<>(records.size());
        for (LearningLesson learningLesson : records) {
            LearningLessonVO vo = BeanUtil.toBean(learningLesson, LearningLessonVO.class);
            vo.setCourseName(courseInfoMap.get(learningLesson.getCourseId()).getName());
            vo.setCourseCoverUrl(courseInfoMap.get(learningLesson.getCourseId()).getCoverUrl());
            vo.setSections(courseInfoMap.get(learningLesson.getCourseId()).getSectionNum());
            vos.add(vo);
        }


        // 4.返回结果
        return PageDTO.of(lessonPage, vos);
    }

    /**
     * 根据id集合查询课程信息，并转为Map
     * @param courseIds
     * @return
     */
    private Map<Long, CourseSimpleInfoDTO> selectLessonAnd2Map(Set<Long> courseIds) {
        List<CourseSimpleInfoDTO> courseInfoList = courseClient.getSimpleInfoList(courseIds);
        if (ObjectUtil.isEmpty(courseInfoList)) {
            log.warn("课程信息查询异常");
        }
        Map<Long, CourseSimpleInfoDTO> courseInfoMap = courseInfoList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, courseSimpleInfoDTO -> courseSimpleInfoDTO));
        return courseInfoMap;
    }

    /**
     * 查询最近学习课程
     *
     * @return
     */
    @Override
    public LearningLessonVO nowLearningLesson() {
        // 1.获取userId
        Long userId = UserContext.getUser();
        if (ObjectUtil.isEmpty(userId)) {
            log.warn("用户信息获取失败");
            return null;
        }

        // 2.根据userId查询最近学习课程
        LearningLesson lesson = this.lambdaQuery().eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getStatus, LEARNING)
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")
                .one();

        // 查询已报名的总课程数
        Integer count = this.lambdaQuery().eq(LearningLesson::getUserId, userId).count();

        // 3.根据课程id查询课程信息
        List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(Collections.singletonList(lesson.getCourseId()));
        if (ObjectUtil.isEmpty(simpleInfoList)) {
            log.warn("课程信息查询异常");
            return null;
        }

        // 查询最近学习的小节名
        // 查询最近学习的小节序号
        List<CataSimpleInfoDTO> cataSimpleInfoDTOS = catalogueClient.batchQueryCatalogue(Collections.singletonList(lesson.getLatestSectionId()));
        if (ObjectUtil.isEmpty(cataSimpleInfoDTOS)) {
            log.warn("小节信息查询异常");
            throw new BizIllegalException("目录信息不存在，无法查询课表");
        }


        // 3.组装数据
        LearningLessonVO vo = BeanUtil.toBean(lesson, LearningLessonVO.class);
        vo.setCourseName(simpleInfoList.get(0).getName());
        vo.setCourseCoverUrl(simpleInfoList.get(0).getCoverUrl());
        vo.setSections(simpleInfoList.get(0).getSectionNum());
        vo.setCourseAmount(count);
        vo.setLatestSectionIndex(cataSimpleInfoDTOS.get(0).getCIndex());
        vo.setLatestSectionName(cataSimpleInfoDTOS.get(0).getName());


        // 4.返回结果
        return vo;
    }

    /**
     * 删除课程表
     *
     * @param orderBasicDTO
     */
    @Override
    public void delete(OrderBasicDTO orderBasicDTO) {
        // 1.根据课程id，userId查询课程列表
        List<LearningLesson> lessons = this.lambdaQuery().in(LearningLesson::getCourseId, orderBasicDTO.getCourseIds())
                .in(LearningLesson::getUserId, orderBasicDTO.getUserId()) // 课程id集合
                .list();
        if (ObjectUtil.isEmpty(lessons)) {
            log.warn("用户没有课程");
            return;
        }

        // 2.删除课程列表
        this.removeByIds(lessons);
    }

    /**
     * 根据课程id删除课程表
     *
     * @param courseId
     */
    @Override
    public void deleteLessonCourseId(Long courseId) {
        // 1.获取用户id
        Long userId = UserContext.getUser();

        // 2.根据课程id，userId删除课程列表
        this.lambdaUpdate().eq(LearningLesson::getCourseId, courseId)
                .eq(LearningLesson::getUserId, userId)
                .remove();
    }

    /**
     * 检查课程是否有效
     *
     * @param courseId
     * @return
     */
    @Override
    public Long checkLessonValid(Long courseId) {
        LearningLesson lesson = getLearningLesson(courseId);
        if (ObjectUtil.isEmpty(lesson)) {
            log.warn("用户没有课程");
            return null;
        }

/*        LocalDateTime expireTime = lesson.getExpireTime();
        if (ObjectUtil.isNotNull(expireTime) && expireTime.isBefore(LocalDateTime.now())) {
            // 课程已过期
            log.warn("课程已过期");
            return null;
        }*/

        if (LessonStatus.EXPIRED.equalsValue(lesson.getStatus())) {
            // 课程已过期
            log.warn("课程已过期");
            return null;
        }
        return courseId;
    }

    /**
     * 根据cuoreId 查询是否有课程
     *
     * @param courseId
     * @return
     */
    private LearningLesson getLearningLesson(Long courseId) {
        if (ObjectUtil.isEmpty(courseId)) {
            log.warn("课程id为空");
            return null;
        }
        // 获取用户id
        Long userId = UserContext.getUser();
        if (ObjectUtil.isEmpty(userId)) {
            log.warn("获取用户信息失败");
            return null;
        }

        // 根据课程id，userId查询课程列表
        LearningLesson lesson = this.lambdaQuery().eq(LearningLesson::getCourseId, courseId)
                .eq(LearningLesson::getUserId, userId)
                .one();

        if (ObjectUtil.isEmpty(lesson)) {
            // 没有该课程
            return null;
        }
        return lesson;
    }

    /**
     * 查询课表中指定课程状态
     *
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonVO checkLessonStatus(Long courseId) {
        // 根据课程id查询是否有该课程
        LearningLesson learningLesson = this.getLearningLesson(courseId);

        if (ObjectUtil.isEmpty(learningLesson)) {
            log.warn("用户没有该课程");
            return null;
        }
        return BeanUtil.toBean(learningLesson, LearningLessonVO.class);
    }

    /**
     * 统计课程的学习人数
     *
     * @param courseId
     * @return
     */
    @Override
    public Integer countLearningLessonByCourse(Long courseId) {
        if (ObjectUtil.isNotEmpty(courseId)) {
            return this.lambdaQuery().eq(LearningLesson::getCourseId, courseId)
                    .ne(LearningLesson::getStatus, LessonStatus.EXPIRED)
                    .count().intValue();
        }
        return 0;
    }

    /**
     * 创建/修改 学习计划
     *
     * @param learningPlanDTO
     */
    @Override
    public void addLearningPlan(LearningPlanDTO learningPlanDTO) {
        this.lambdaUpdate().eq(LearningLesson::getCourseId, learningPlanDTO.getCourseId())
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .in(LearningLesson::getStatus, NOT_BEGIN, LEARNING)
                .set(LearningLesson::getWeekFreq, learningPlanDTO.getFreq())
                .set(LearningLesson::getPlanStatus, PLAN_RUNNING)
                .update();
    }

    /**
     * @param pageQuery
     * @return
     */
    @Override
    public LearningPlanPageVO queryLearningPlan(PageQuery pageQuery) {
        // 1.查询课程表
        Page<LearningLesson> page = this.lambdaQuery()
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .eq(LearningLesson::getPlanStatus, PLAN_RUNNING)
                .in(LearningLesson::getStatus, NOT_BEGIN, LEARNING)
                .page(pageQuery.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = page.getRecords();

        if (ObjectUtil.isEmpty(records)){
            throw new BizIllegalException("未查询到课程");
        }

        // 2.获取课程id集合
        Set<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());

        // 3.根据课程id集合查询课程信息
        Map<Long, CourseSimpleInfoDTO> courseSimpleInfoDTOMap = this.selectLessonAnd2Map(courseIds);

        // 4.查询本周学习的小节数量
        QueryWrapper<LearningRecord> wrapper = new QueryWrapper<>();
        wrapper.select("lesson_id", "count(id) as sum")
                .eq("user_id", UserContext.getUser())
                .eq("finished", true)
                .between("create_time", DateUtils.getWeekBeginTime(LocalDate.now()), DateUtils.getWeekBeginTime(LocalDate.now()))
                .groupBy("lesson_id");
        List<Map<String, Object>> maps = learningRecordMapper.selectMaps(wrapper);
        Map<Long, Long> recordMap = maps.stream().collect(Collectors.toMap(k -> (Long) k.get("lesson_id"), k -> (Long) k.get("sum")));

        // 5. 统计本周计划学习小节量
        int planCount = records.stream().mapToInt(LearningLesson::getWeekFreq).sum();

        // 6.统计本周实际学习小节量
        int count = recordMap.values().stream().mapToInt(Long::intValue).sum();

        // 7.组装列表数据
        LearningPlanPageVO learningPlanPageVO = new LearningPlanPageVO();

        List<LearningPlanVO> learningPlanVOS = BeanUtil.copyToList(records, LearningPlanVO.class);
        for (LearningPlanVO learningPlanVO : learningPlanVOS) {
            learningPlanVO.setCourseName(courseSimpleInfoDTOMap.get(learningPlanVO.getCourseId()).getName());
            learningPlanVO.setSections(courseSimpleInfoDTOMap.get(learningPlanVO.getCourseId()).getSectionNum());
            learningPlanVO.setWeekLearnedSections(recordMap.getOrDefault(learningPlanVO.getCourseId(),0L).intValue());
        }

        learningPlanPageVO.setWeekFinished(count);
        learningPlanPageVO.setWeekTotalPlan(planCount);
        learningPlanPageVO.pageInfo(page.getTotal(),page.getPages(),learningPlanVOS);

        return learningPlanPageVO;
    }


}
